package org.masterview.user.client.ui;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
import org.masterview.user.client.data.Filter;
import org.masterview.user.client.data.DataSourceImpl;
import org.masterview.user.client.data.DataSourceListener;
import org.masterview.user.client.data.DataSource;
import org.masterview.user.client.SortConstants;

import java.util.ArrayList;
import java.util.List;

public class Grid extends FlexTable {
    protected int sortedColumn = -1;
    protected String sortType;
    protected List columns;
    protected boolean filteringEnabled;

    public boolean isFilteringEnabled() {
        return filteringEnabled;
    }

    public void setFilteringEnabled(boolean filteringEnabled) {
        this.filteringEnabled = filteringEnabled;
    }

    /**
     * <p>Data source for the table (used to fetch initialData).</p>
     */
    protected DataSource dataSource;

    /**
     * <p>Object to render table's cells.</p>
     */
    protected GridRenderer renderer;

    public DataSource getDataSource() {
        return dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public GridRenderer getRenderer() {
        return renderer;
    }

    public void setRenderer(GridRenderer renderer) {
        this.renderer = renderer;
    }

    /**
     * Public constructor for the grid.
     *
     * @param dataSource source of the initialData items for the grid to render.
     */
    public Grid(DataSource dataSource) {
        setDataSource(dataSource);
        dataSource.addDataSourceListener(new DataSourceListener() {
            public void onDataChanged(int readItemsCount, int allItemsCount) {
                renderItems();
            }
        });


        renderer = new GridRendererImpl();

        setRenderer(renderer);
        getRenderer().setGrid(this);
        setStyleName(getRenderer().getGridStyle());

        columns = new ArrayList();
        sortType = SortConstants.NO_SORTING;

        addTableListener(new SorterTableListener(this));

        sinkEvents(Event.ONMOUSEOVER);
        sinkEvents(Event.ONMOUSEOUT);
    }

    /**
     * Renders the grid.
     */
    public void render() {
        getRenderer().renderHeader(columns);

        if (isFilteringEnabled()) {
            getRenderer().renderFilters(columns);
        }

        renderItems();
    }

    public void renderItems() {
        int rowNumber = getFirstRowWithDataNumber();
        int rowsForItems = getRenderer().renderItems(columns, dataSource);

        int neededRowsCount = rowNumber + rowsForItems;
        deleteExcessRows(neededRowsCount);
    }

    /**
     * <p>Deletes redundant rows left from previous rendering (if necessary).</p>
     *
     * @param neededRowsCount
     */
    protected void deleteExcessRows(int neededRowsCount) {
        int rowsToDelete = getRowCount() - neededRowsCount;

        while (rowsToDelete > 0) {
            removeRow(getRowCount() - 1);
            rowsToDelete--;
        }
    }

    public int getFirstRowWithDataNumber() {
        if (isFilteringEnabled()) {
            return 2;
        } else {
            return 1;
        }
    }

    /**
     * <p>Method is overriden to render the grid after loading.</p>
     */
    protected void onLoad() {
        render();
    }

    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);


        switch (DOM.eventGetType(event)) {
            case Event.ONMOUSEOVER: {
                Element td = getEventTargetCell(event);
                if (td == null) {
                    return;
                }

                Element tr = DOM.getParent(td);
                Element body = DOM.getParent(tr);
                int row = DOM.getChildIndex(body, tr);

                getRenderer().highlightRow(row);

                break;
            }
            case Event.ONMOUSEOUT: {
                Element td = getEventTargetCell(event);
                if (td == null) {
                    return;
                }

                Element tr = DOM.getParent(td);
                Element body = DOM.getParent(tr);
                int row = DOM.getChildIndex(body, tr);

                getRenderer().cancelRowHighlighting(row);
                break;
            }
            default: {
                // Do nothing
            }
        }
    }

    public void appendColumn(Column column) {
        columns.add(column);
    }

    public void removeColumn(int index) {
        columns.remove(index);
    }

    public void removeColumn(Column column) {
        columns.remove(column);
    }

    public void clearColumns(Column column) {
        columns.clear();
    }

    public Column getColumn(int index) {
        return (Column) columns.get(index);
    }

    public void sortColumn(int columnIndex) {
        if (sortedColumn != -1) {
            getCellFormatter().setStyleName(0, sortedColumn, getRenderer().getHeaderCellStyle());
        }

        Column columnToSort = (Column) columns.get(columnIndex);

        if (!columnToSort.isSortable()) {
            return;
        }

        dataSource.setPropertyToSort(columnToSort.getPropertyName());

        if (sortType.equals(SortConstants.NO_SORTING)) {
            getCellFormatter().setStyleName(0, columnIndex, getRenderer().getCellAscendingSortingStyle());

            sortType = SortConstants.SORT_ASC;
            dataSource.setSortType(SortConstants.SORT_ASC);
        } else if (sortType.equals(SortConstants.SORT_ASC)) {
            getCellFormatter().removeStyleName(0, sortedColumn, getRenderer().getCellAscendingSortingStyle());
            getCellFormatter().setStyleName(0, columnIndex, getRenderer().getCellDescendingSortingStyle());

            sortType = SortConstants.SORT_DESC;
            dataSource.setSortType(SortConstants.SORT_DESC);
        } else if (sortType.equals(SortConstants.SORT_DESC)) {
            getCellFormatter().removeStyleName(0, sortedColumn, getRenderer().getCellDescendingSortingStyle());            

            sortType = SortConstants.NO_SORTING;
            dataSource.setSortType(SortConstants.NO_SORTING);
        }

        sortedColumn = columnIndex;
    }

    /**
     * <p>Applies a filter (just a plain expression string) to a column.</p>
     *
     * @param column a column to which a filter will be applied.
     * @param text   a filter expression to apply.
     */
    public void applyFilter(Column column, String text) {
        dataSource.clearFiltersByProperty(column.getPropertyName());
        dataSource.addFilter(new Filter(column.getPropertyName(), text));
    }

    public void removeFilter(Column column) {
        dataSource.clearFiltersByProperty(column.getPropertyName());        
    }
}